// This compound command was inspired by the cool hack Steve Drucker
// used in his PhD system to take some small (usually 8 or 16) views of a
// "real" object and map it into a 3D virtual environment.  This
// technique probably originates in flight simulators and/or video games,
// but I'm stealing it from Steve, so that's who I'm crediting with the
// idea...

// The 


// WW3DVideoHack {list of texture files} width height

// The difficulty here is that I need to determine at renderSelf: time
// where I am in relation to the camera's view point.  If I can do that,
// I'm golden.

// what I need to do is transform the vector (0, 0, 1) 



#import "WW3DVideoHack.h"
#import "RIBTransformBegin.h"
#import "RIBScale.h"
#import "RIBSphere.h"
#import "RIBTransformEnd.h"
#import "WWSample.h"
#import "WWEveParser.h"

@implementation WW3DVideoHack

+ initialize { return [WW3DVideoHack setVersion:1], self; }

- init
{
  [super init];

  radius = 1.0;
  zMin = -1.0;
  zMax =  1.0;
  xScale = yScale = zScale = 1.0;
  ribCommandList  = [[RIBCommandList alloc] init];
  dirtyBoundingBox = YES;
  
  return self;
}


- awake
{
    [super awake];
    dirtyBoundingBox = YES;
    return self;
}


- setYScale:(RtFloat)newYScale
{
   yScale = newYScale;
   xScale = zScale = 1./sqrt((double)yScale);
   return self;
}

- setRadius:(RtFloat)newRadius 
     zMin:(RtFloat)newZMin zMax:(RtFloat)newZMax 
     thetaMax:(RtFloat)newThetaMax yScale:(RtFloat)newYScale
     n:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector
     printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   id  sphereObj;


   radius = newRadius;
   zMin = newZMin;
   zMax = newZMax;
   thetaMax = newThetaMax;
   [self setYScale:newYScale];

   // I should make sure there is some non-whitespace text before I waste my time...
   [ribCommandList empty];

   sphereObj = [[RIBSphere alloc] init]; 
   [sphereObj setRadius:radius zMin:zMin zMax:zMax thetaMax:thetaMax
                    n:newN tokens:newTokens parms:newParms archiveVector:newArchiveVector printfTypeVector:newPrintfTypeVector printfNVector:newPrintfNVector];
   [sphereObj setMyShape:myShape];

   [ribCommandList insertObject:sphereObj at:0];
   [ribCommandList insertObject:[[[[RIBScale alloc] init] setSX:xScale sy:yScale sz:zScale] setMyShape:myShape] at:0];
   [ribCommandList insertObject:[[[RIBTransformBegin alloc] init] setMyShape:myShape] at:0];
   [ribCommandList addObject:[[[RIBTransformEnd alloc] init] setMyShape:myShape]];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- setRadius:(RtFloat)newRadius { radius = newRadius; dirtyBoundingBox = TRUE; return self; }
- setZMin:(RtFloat)newZMin { zMin = newZMin; dirtyBoundingBox = TRUE; return self; }
- setZMax:(RtFloat)newZMax { zMax = newZMax; dirtyBoundingBox = TRUE; return self; }

- (RtFloat)radius { return radius; } 
- (RtFloat)zMin { return zMin; }
- (RtFloat)zMax { return zMax; }

- free
{
    [ribCommandList free];
    return [super free];
}

- (BOOL)hasBoundingBox		{ return YES; }
- (BOOL)isMotionBlurrable	{ return YES; }
- (BOOL)isCompoundCommand	{ return YES; }

- setBoundingBox:(RtBound *)newBoundingBox
{
    N3D_CopyBound(*newBoundingBox, boundingBox);
    return self;
}

- calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime  
{  
    dirtyBoundingBox = NO;
    [ribCommandList calculateBoundingBoxStartingAt:shutterOpenTime
                    endingAt:shutterCloseTime];
    return [self setBoundingBox:[ribCommandList boundingBox]];
}

- (RtBound *)boundingBox 
{ 
    if (dirtyBoundingBox) { 
	[self calculateBoundingBoxStartingAt:[[myShape sceneClock] timestamp]
              endingAt:[[myShape sceneClock] timestamp]];
    }
    return &boundingBox; 
}

- (float)lastSampleIsAt { return 0.0; }

- (unsigned long int)maxSampleBandwidth { return [ribCommandList maxSampleBandwidth]; }

- setMyShape:shape
{
    myShape = shape;
    return self;
}

- shape
{
    return myShape;
}

- renderMaps:(WW3DCamera *)camera
    startingAt:(RtFloat)shutterOpenTime
    endingAt:(RtFloat)shutterCloseTime usingStream:(NXStream *)ns
{
    return self;
}

- renderMaps:(WW3DCamera *)camera usingStream:(NXStream *)ns
{
    return self;
}

- renderMaps:(WW3DCamera *)camera
    startingAt:(RtFloat)shutterOpenTime
    endingAt:(RtFloat)shutterCloseTime
{
    return self;
}

- renderMaps:(WW3DCamera *)camera
{
    return self;
}

- renderSelf:(WW3DCamera *)camera
    startingAt:(RtFloat)shutterOpenTime
    endingAt:(RtFloat)shutterCloseTime
{
    return [ribCommandList renderSelf:camera
			   startingAt:shutterOpenTime
			   endingAt:shutterCloseTime];
}

- renderSelf:(WW3DCamera *)camera
{
    return [ribCommandList renderSelf:camera];
}

- preRenderSelf:(WW3DCamera *)camera
    startingAt:(RtFloat)shutterOpenTime
    endingAt:(RtFloat)shutterCloseTime
{
    return [ribCommandList preRenderSelf:camera
			   startingAt:shutterOpenTime
			   endingAt:shutterCloseTime];
}

- preRenderSelf:(WW3DCamera *)camera
{
    return [ribCommandList preRenderSelf:camera];
}

- transformCTM:(RtMatrix)aMatrix 
    startingAt:(RtFloat)shutterOpenTime
    endingAt:(RtFloat)shutterCloseTime 
{
    return [ribCommandList transformCTM:aMatrix
                           startingAt:shutterOpenTime
			   endingAt:shutterCloseTime]; 
}

// methods to make me look like a compound command:
- (int)count {  return [ribCommandList count]; }
- objectAt:(int)i {  return [ribCommandList objectAt:i]; }


// WavesWorld archiving:
// writeEve:(NXStream *)stream
// writeScene:(NXStream *)stream

- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
    int  i;
    
    for (i = 0; i < tab; i++) {
	NXPrintf(stream, "\t");
    }
    NXPrintf(stream, "WW3DVideoHack %f %f %f %f", radius, zMin, zMax, thetaMax, yScale);
    return self;
}

- writeScene:(NXStream *)stream atTabLevel:(int)tab
{
    int  i;
    
    for (i = 0; i < tab; i++) {
	NXPrintf(stream, "\t");
    }
    NXPrintf(stream, "WW3DVideoHack %f %f %f %f", radius, zMin, zMax, thetaMax, yScale);
    return self;
}

#define typeVector "ffff"
#define typeValues &radius, &zMin, &zMax, &yScale

- read:(NXTypedStream *)stream 
{
    int version;
    
    [super read:stream];
    version = NXTypedStreamClassVersion(stream,"WW3DVideoHack");
    if (version == 0) NXReadTypes(stream, "i", &version), version = 1;
    if (version == 1)
    {  NXReadTypes(stream, typeVector, typeValues);
       NXReadArray(stream, "f", 6, boundingBox);
       myShape = NXReadObject(stream);
       xScale = zScale = 1./sqrt((double)yScale);
    }

    return self;
}

- write:(NXTypedStream *)stream 
{
    [super write:stream];
    NXWriteTypes(stream, typeVector, typeValues);
    NXWriteArray(stream, "f", 6, boundingBox);
    NXWriteObjectReference(stream, myShape);
    return self;
}

- (BOOL)theSameAs:otherRIBCommand
{
  // WAVE: really should try and return YES, but for now...
  return NO;
}

- (BOOL)isMoot
{
  return NO;
}

// boy, this is dumb... This is to get around the stupid warnings from the compiler - ask wave for details
- class { return [super class]; }

@end
